package by.ad.servlet;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URISyntaxException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import by.ad.CachedPage;
import by.ad.xml.IGetXmlData;
import by.ad.xml.XmlUtils;
import static by.ad.appengine.Utils.isProductionEnv;
import static by.ad.HttpUtils.checkCookieValue;
import static by.ad.HttpUtils.CONTENT_TYPE;
import static by.ad.HttpUtils.DATE;
import static by.ad.HttpUtils.EXPIRES;
import static by.ad.HttpUtils.TEXT_XML;
import static by.ad.HttpUtils.TEXT_HTML;
import static by.ad.HttpUtils.HttpDateHeaderFormat;
import static by.ad.xml.XmlUtils.createTransformer;
import static by.ad.xml.XmlUtils.loadXml;
import static com.google.common.base.Charsets.UTF_8;

@SuppressWarnings("serial")
public class XsltServlet
  extends HttpServlet 
{
  
  @Override
  protected void service(final HttpServletRequest req, final HttpServletResponse resp)
    throws ServletException, IOException 
  {
    final String uri = req.getRequestURI();
    
    //
    // determine the ability of XSLT
    //
    
    final Boolean clientCanXslt = checkCookieValue(req, "can-xslt", "1");
    final Boolean clientWantsXml = "xml".equalsIgnoreCase(req.getParameter("alt")); 
    final Boolean clientWantsHtml = "html".equalsIgnoreCase(req.getParameter("alt"));
    
    //
    // prepare data
    //
    
    final String iGetXmlData = this instanceof IGetXmlData 
      ? ((IGetXmlData) this).getXmlData(req) : null;
    final Boolean sendRawXmlData = (iGetXmlData != null) 
      && (clientCanXslt || clientWantsXml) && !clientWantsHtml; 
    final String configContentType = getServletConfig().getInitParameter(CONTENT_TYPE);
    final String contentType = sendRawXmlData ? TEXT_XML : 
      (configContentType != null ? configContentType : TEXT_HTML); 
    String content;
    
    if (sendRawXmlData) {
      // send xml
      content = iGetXmlData;
    } else {
      // send html/xslt
      final StringWriter writer = new StringWriter();
      try {
        final Transformer transformer = getTransformer();
        transformer.setParameter("detect-xslt", 
          "true".equalsIgnoreCase(req.getSession().getServletContext().getInitParameter("detect-xslt")));
        
        transformer.transform(loadXml( 
          iGetXmlData != null
            ? new InputSource(new StringReader(iGetXmlData)) {{ setSystemId(uri); }}
            : XmlUtils.getInputSource(uri)), new StreamResult(writer));
      } catch (URISyntaxException e) {
        throw new IOException(e);
      } catch (SAXException e) {
        throw new ServletException(e);
      } catch (TransformerException e) {
        throw new ServletException(e);
      } catch (ParserConfigurationException e) {
        throw new ServletException(e);
      }
      
      content = writer.toString().trim();
    }
    
    req.setAttribute(CACHED_PAGE, new CachedPage(content, contentType));
        
    //
    // do process request
    //
    
    resp.setCharacterEncoding(UTF_8.name());
    resp.setContentType(contentType);
    
    // page marked as "already expired"
    final String alreadyExpired = HttpDateHeaderFormat.getValue();
    resp.setHeader(EXPIRES, alreadyExpired);
    resp.setHeader(DATE, alreadyExpired);
    //resp.setHeader(CACHE_CONTROL, "no-cache, must-revalidate");
    
    super.service(req, resp);
  }

  @Override
  protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
    throws IOException, ServletException
  {
    resp.getWriter().write(((CachedPage) req.getAttribute(CACHED_PAGE)).content);
  }

  /*
  @Override
  protected long getLastModified(final HttpServletRequest req)
  {
    // get jar creation date
  }
  */
  
  // xslt transformer
  protected Transformer getTransformer()
    throws TransformerConfigurationException, SAXException, ParserConfigurationException, IOException, URISyntaxException 
  {
    if (transformer == null || !isProductionEnv) {
      final String templateName = getServletConfig().getInitParameter("template");
      transformer = createTransformer(templateName);
      transformer.setParameter("production-env", isProductionEnv);
    }
    
    return transformer;
  }
  
  private Transformer transformer;
  
  // custom request attributes
  private static final String CACHED_PAGE = "cachedPage";
}
